﻿using System;
using System.ComponentModel;
using System.Net;
using System.Threading;
using System.IO.IsolatedStorage;
using System.Security.Cryptography;
using System.Text;
using System.Diagnostics;

namespace Galaktika.BI.Runtime.Services
{
    /// <summary>
    /// Предоставляет сервис, используемый для вызова серверных команд.
    /// </summary>
    public class SyncDataService : IDisposable
    {
        DataServiceSoapClient m_Client;
        private static bool m_enableClientCaching = false;

        /// <summary>
        /// Предоставляет сервис, используемый для вызова серверных команд.
        /// </summary>
        public SyncDataService()
        {
            m_Client = Galaktika.BI.Silverlight.ServiceManager.CreateService<DataServiceSoapClient, DataServiceSoap>();
            m_Client.InvokeCommandCompleted += new EventHandler<InvokeCommandCompletedEventArgs>(client_InvokeCommandCompleted);
            m_Client.GetDataBinderStatusHashCompleted += new EventHandler<GetDataBinderStatusHashCompletedEventArgs>(client_GetDataBinderStatusHashCompleted);
        }

        public static void EnableCache()
        {
            m_enableClientCaching = true;
        }

        /// <summary>
        /// Синхронно исполняет команду, описанную в <paramref name="schema"/> на сервере.
        /// Метод должен вызываться <b>только</b> из фонового потока.
        /// </summary>
        /// <param name="schema">Объект, описывающий схему вызова команды.</param>
        /// <exception cref="Galaktika.BI.Runtime.Services.ServerException" />
        /// <returns>Объект, описывающий результат вызова команды.</returns>
        public InvokeResultSchema InvokeCommand(InvokeSchema schema)
        {
            InvokeResultSchema resultSchema = new InvokeResultSchema();
            resultSchema = this.InvokeCommand(schema, null);
            return resultSchema;
        }

        private ManualResetEvent m_Event = new ManualResetEvent(false);
        private ManualResetEvent m_Event2 = new ManualResetEvent(false);
        private InvokeResultSchema m_Res;
        private string m_BinderStatusHash;
        /// <summary>
        /// Синхронно исполняет команду, описанную в <paramref name="schema"/> на сервере.
        /// Метод должен вызываться <b>только</b> из фонового потока.
        /// </summary>
        /// <param name="schema">Объект, описывающий схему вызова команды.</param>
        /// <param name="state">
        /// Пользовательский объект, 
        /// доступ к которому необходимо получить в обработчике результата
        /// выполнения команды.
        /// </param>
        /// <exception cref="Galaktika.BI.Runtime.Services.ServerException" />
        /// <returns>Объект, описывающий результат вызова команды.</returns>
        public InvokeResultSchema InvokeCommand(InvokeSchema schema, object state)
        {
            m_Res = new InvokeResultSchema();
            if (m_enableClientCaching)
            {
                m_Client.GetDataBinderStatusHashAsync(schema[KnownInvokeArgs.ContextId]);
                m_Event2.WaitOne();
                string hash = String.Format("BIcache{0}{1}", schema.GetSHA1Hash(), m_BinderStatusHash);
                if (ClientCache.Instance.Contains(hash))
                {
                    m_Res = (InvokeResultSchema)ClientCache.Instance[hash];
                }
                else
                {
                    m_Client.InvokeCommandAsync(schema, state);
                    m_Event.WaitOne();
                    ClientCache.Instance[hash] = m_Res;
                }
            }
            else
            {
                m_Client.InvokeCommandAsync(schema, state);
                m_Event.WaitOne();
            }

            if (this.Error != null)
            {
                throw this.Error;
            }
            if (m_Res.ContentType == KnownContentTypes.Error)
            {
                throw new ServerException(m_Res.Content);
            }

            return m_Res;
        }

        /// <summary>
        /// Возвращает исключение, возникшее при обращении к серверу.
        /// Если исключения не произошло возвращает null.
        /// </summary>
        public Exception Error { get; private set; }

        void client_InvokeCommandCompleted(object sender, InvokeCommandCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                m_Res = e.Result;
            }
            else
            {
                this.Error = e.Error;
            }
            m_Event.Set();
        }

        void client_GetDataBinderStatusHashCompleted(object sender, GetDataBinderStatusHashCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                m_BinderStatusHash = e.Result;
            }
            else
            {
                this.Error = e.Error;
            }
            m_Event2.Set();
        }

        #region IDisposable Members

        public void Dispose()
        {
            try
            {
                m_Client.Abort();
            }
            catch
            {
                // до жопы
            }
        }

        #endregion

        /*
        DataServiceSoapClient m_Client;
        public SyncDataService()
        {
            m_Client = Galaktika.BI.Silverlight.ServiceManager.CreateService<DataServiceSoapClient, DataServiceSoap>();
            m_Client.InvokeCommandCompleted += new EventHandler<InvokeCommandCompletedEventArgs>(client_InvokeCommandCompleted);
        }

        public InvokeResultSchema InvokeCommand(InvokeSchema schema)
        {
            return this.InvokeCommand(schema, null);
        }

        private ManualResetEvent m_Event = new ManualResetEvent(false);
        public Exception Error { get; private set; }
        private InvokeResultSchema m_Res;
        public InvokeResultSchema InvokeCommand(InvokeSchema schema, object state)
        {
            m_Res = new InvokeResultSchema();
            m_Client.InvokeCommandAsync(schema, state);
            m_Event.WaitOne();

            return m_Res;
        }

        void client_InvokeCommandCompleted(object sender, InvokeCommandCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                m_Res = e.Result;
            }
            else
            {
                this.Error = e.Error;
            }
            m_Event.Set();
        }

        */
    }

    public class ClientCache
    {
        private static IsolatedStorageSettings m_cache = null;
        private static object sync = new object();

        private ClientCache() {}

        public static IsolatedStorageSettings Instance
        {
            get
            {
                if (m_cache == null) 
                {
                    lock (sync)
                    {
                        if (m_cache == null)
                            m_cache = IsolatedStorageSettings.ApplicationSettings;
                    }
                }
                return m_cache;
            }
        }
    }
}
